#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "StructuredActivityNode.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class Action;
class ActivityNode;
class ExecutableNode;
class InputPin;
class OutputPin;

/// <summary>
/// LoopNode
/// A LoopNode is a StructuredActivityNode that represents an iterative loop with
/// setup, test, and body sections.
/// </summary>
class UMLMODEL_EXPORT LoopNode : public StructuredActivityNode {

public: // Constructors and destructors
    explicit LoopNode(const QString& umlType="uml::LoopNode");

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// allActions
    /// Return only this LoopNode. This prevents Actions within the LoopNode from having
    /// their OutputPins used as bodyOutputs or decider Pins in containing LoopNodes or
    /// ConditionalNodes.
    /// </summary>
    shared_ptr<vector<Action>> allActions() const override;

    /// <summary>
    /// sourceNodes
    /// Return the loopVariable OutputPins in addition to other source nodes for the
    /// LoopNode as a StructuredActivityNode.
    /// </summary>
    shared_ptr<vector<ActivityNode>> sourceNodes() const override;

public: // Properties
    /// <summary>
    /// bodyOutputs getter
    /// The OutputPins on Actions within the bodyPart, the values of which are moved to
    /// the loopVariable OutputPins after the completion of each execution of the
    /// bodyPart, before the next iteration of the loop begins or before the loop exits.
    /// </summary>
    vector<shared_ptr<OutputPin>>& bodyOutputs();

    /// <summary>
    /// bodyParts getter
    /// The set of ExecutableNodes that perform the repetitive computations of the loop.
    /// The bodyPart is executed as long as the test section produces a true value.
    /// </summary>
    vector<shared_ptr<ExecutableNode>>& bodyParts();

    /// <summary>
    /// decider getter
    /// An OutputPin on an Action in the test section whose Boolean value determines
    /// whether to continue executing the loop bodyPart.
    /// </summary>
    shared_ptr<OutputPin> decider() const;

    /// <summary>
    /// decider setter
    /// An OutputPin on an Action in the test section whose Boolean value determines
    /// whether to continue executing the loop bodyPart.
    /// </summary>
    void setDecider(shared_ptr<OutputPin> value);

    /// <summary>
    /// isTestedFirst getter
    /// If true, the test is performed before the first execution of the bodyPart. If
    /// false, the bodyPart is executed once before the test is performed.
    /// </summary>
    bool isTestedFirst() const;

    /// <summary>
    /// isTestedFirst setter
    /// If true, the test is performed before the first execution of the bodyPart. If
    /// false, the bodyPart is executed once before the test is performed.
    /// </summary>
    void setIsTestedFirst(bool value);

    /// <summary>
    /// loopVariables getter
    /// A list of OutputPins that hold the values of the loop variables during an
    /// execution of the loop. When the test fails, the values are moved to the result
    /// OutputPins of the loop.
    /// </summary>
    vector<shared_ptr<OutputPin>>& loopVariables();

    /// <summary>
    /// loopVariableInputs getter
    /// A list of InputPins whose values are moved into the loopVariable Pins before the
    /// first iteration of the loop.
    /// </summary>
    vector<shared_ptr<InputPin>>& loopVariableInputs();

    /// <summary>
    /// results getter
    /// A list of OutputPins that receive the loopVariable values after the last
    /// iteration of the loop and constitute the output of the LoopNode.
    /// </summary>
    vector<shared_ptr<OutputPin>>& results();

    /// <summary>
    /// setupParts getter
    /// The set of ExecutableNodes executed before the first iteration of the loop, in
    /// order to initialize values or perform other setup computations.
    /// </summary>
    vector<shared_ptr<ExecutableNode>>& setupParts();

    /// <summary>
    /// tests getter
    /// The set of ExecutableNodes executed in order to provide the test result for the
    /// loop.
    /// </summary>
    vector<shared_ptr<ExecutableNode>>& tests();

private: // Fields
    /// <summary>
    /// bodyOutputs
    /// The OutputPins on Actions within the bodyPart, the values of which are moved to
    /// the loopVariable OutputPins after the completion of each execution of the
    /// bodyPart, before the next iteration of the loop begins or before the loop exits.
    /// </summary>
    vector<shared_ptr<OutputPin>> bodyOutputs_;

    /// <summary>
    /// bodyParts
    /// The set of ExecutableNodes that perform the repetitive computations of the loop.
    /// The bodyPart is executed as long as the test section produces a true value.
    /// </summary>
    vector<shared_ptr<ExecutableNode>> bodyParts_;

    /// <summary>
    /// decider
    /// An OutputPin on an Action in the test section whose Boolean value determines
    /// whether to continue executing the loop bodyPart.
    /// </summary>
    shared_ptr<OutputPin> decider_;

    /// <summary>
    /// isTestedFirst
    /// If true, the test is performed before the first execution of the bodyPart. If
    /// false, the bodyPart is executed once before the test is performed.
    /// </summary>
    const static bool isTestedFirst_default_;
    bool isTestedFirst_= isTestedFirst_default_;

    /// <summary>
    /// loopVariables
    /// A list of OutputPins that hold the values of the loop variables during an
    /// execution of the loop. When the test fails, the values are moved to the result
    /// OutputPins of the loop.
    /// </summary>
    vector<shared_ptr<OutputPin>> loopVariables_;

    /// <summary>
    /// loopVariableInputs
    /// A list of InputPins whose values are moved into the loopVariable Pins before the
    /// first iteration of the loop.
    /// </summary>
    vector<shared_ptr<InputPin>> loopVariableInputs_;

    /// <summary>
    /// results
    /// A list of OutputPins that receive the loopVariable values after the last
    /// iteration of the loop and constitute the output of the LoopNode.
    /// </summary>
    vector<shared_ptr<OutputPin>> results_;

    /// <summary>
    /// setupParts
    /// The set of ExecutableNodes executed before the first iteration of the loop, in
    /// order to initialize values or perform other setup computations.
    /// </summary>
    vector<shared_ptr<ExecutableNode>> setupParts_;

    /// <summary>
    /// tests
    /// The set of ExecutableNodes executed in order to provide the test result for the
    /// loop.
    /// </summary>
    vector<shared_ptr<ExecutableNode>> tests_;

};

} // namespace uml
